#include "MBCameraViewFrustum.h"

const double ANG_2_RAD = 3.14159265358979323846 / 180.0;

#include <math.h>

/**
  * Constructs a new MBCameraViewFrustum object defined by six planes
  */
MBCameraViewFrustum::MBCameraViewFrustum()
{
}

/**
  * Destroys the MBCameraViewFrustum object
  */
MBCameraViewFrustum::~MBCameraViewFrustum()
{
}

/**
  * Sets the internal parameters of the perspective camera
  *
  * @param angle Vertical aperture angle of the view-frustum
  * @param ratio Aspect ratio of the clipping planes, width / height
  * @param nearD Distance to the near clipping plane
  * @param farD Distance to the far clipping plane
  */
void MBCameraViewFrustum::SetCamInternals( const double &angle, const double &ratio, const double &nearD, const double &farD )
{
	this->mRatio = ratio;
	this->mAngle = angle;
	this->mNearD = nearD;
	this->mFarD = farD;

	// compute width and height of the near and far plane sections
	mTang = static_cast<double>( tan( ANG_2_RAD * angle * 0.5 ) );
	mNh = nearD * mTang;
	mNw = mNh * ratio; 
	mFh = farD * mTang;
	mFw = mFh * ratio;
}

/**
  * Sets the external parameters of the perspective camera
  *
  * @param position Position of the camera in 3D space
  * @param lookAtPoint Point in 3D space the camera is looking to
  * @param upVector Up-Vector
  */
void MBCameraViewFrustum::SetCamDef( const MBVec3 &position, const MBVec3 &lookAtPoint, const MBVec3 &upVector )
{
	MBVec3 dir;
	MBVec3 mNc;
	MBVec3 fc;
	MBVec3 X;
	MBVec3 Y;
	MBVec3 Z;

	// compute the Z axis of camera
	// this axis points in the opposite direction from
	// the looking direction
	Z = position - lookAtPoint;
	Z.Normalize();

	// X axis of camera with given "up" vector and Z axis
	X = MBVec3::Cross( upVector, Z );
	X.Normalize();

	// the real "up" vector is the cross product of Z and X
	Y = MBVec3::Cross( Z, X );

	// compute the centers of the near and far mPlanes
	mNc = position - (Z * mNearD);
	fc = position - (Z * mFarD);

	mPlanes[NEARP].SetNormalAndPoint( -Z, mNc );
	mPlanes[FARP].SetNormalAndPoint( Z, fc );

	MBVec3 aux;
	MBVec3 normal;

	aux = (mNc + (Y * mNh)) - position;
	aux.Normalize();
	normal = MBVec3::Cross( aux, X );
	mPlanes[TOP].SetNormalAndPoint(normal, mNc + (Y * mNh));

	aux = (mNc - (Y * mNh)) - position;
	aux.Normalize();
	normal = MBVec3::Cross( X, aux );
	mPlanes[BOTTOM].SetNormalAndPoint(normal, mNc - (Y * mNh));
	
	aux = (mNc - (X * mNw)) - position;
	aux.Normalize();
	normal = MBVec3::Cross( aux, Y );
	mPlanes[LEFT].SetNormalAndPoint(normal, mNc - (X * mNw));

	aux = (mNc + (X * mNw)) - position;
	aux.Normalize();
	normal = MBVec3::Cross( Y, aux );
	mPlanes[RIGHT].SetNormalAndPoint(normal, mNc + (X * mNw));
}

/**
  * Returns a boolean value indicating whether the delivered axis aligned box intersects the view frustum or not
  *
  * @param boxMin Minimum point of the box
  * @param boxMax Maximum point of the box
  * @return Boolean value indicating whether the delivered axis aligned box intersects the view frustum or not
  */
bool MBCameraViewFrustum::IsAxisAlignedBoxIntersectingFrustum( const MBVec3 &boxMin, const MBVec3 &boxMax ) const
{
	for (int i = 0; i < 6; i++)
	{
		// is the positive vertex outside?
		// (this implies that the box is outside the whole frustum)
		if (mPlanes[i].IsPositiveVertexOfAxisAlignedBoxOutside( boxMin, boxMax ))
		{
			return false;
		}
	}

	return true;
}